#pragma once

#include <string>
#include <mutex>
#include <memory>
#include <functional>
#include <iostream>
#include <fstream>
#include <filesystem>

#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <sstream>



using namespace std;



namespace LogMudule
{

    enum class LogLevel // 日志等级
    {
        DEFAULT = 0, // 默认  只用于日志过滤
        DEBUG,       // 调试
        NORMAL,      // 正常
        WARNING,     // 警告
        ERROR,       // 错误
        DEADLY       // 致命
    };

    string LogLevelToString(const LogLevel &level) // 把日志等级转换为字符串
    {
        string s;
        switch (level)
        {
        case LogLevel::DEBUG:
            s = "debug";
            break;
        case LogLevel::NORMAL:
            s = "normal";
            break;
        case LogLevel::WARNING:
            s = "warning";
            break;
        case LogLevel::ERROR:
            s = "error";
            break;
        case LogLevel::DEADLY:
            s = "deadly";
            break;

        default:
            s = "unknown";
            break;
        }
        return s;
    }

    string CurrentTime() // 获取当前时间
    {
        time_t time_stamp = time(nullptr); // 获取当前时间戳
        struct tm curr;

        localtime_r(&time_stamp, &curr); // 把时间戳转换成struct tm对象
        char buf[1024];

        snprintf(buf, sizeof(buf), "%4d-%02d-%02d %02d:%02d:%02d", // 年-月-日 时：分：秒
                 curr.tm_year + 1900,
                 curr.tm_mon + 1,
                 curr.tm_mday,
                 curr.tm_hour,
                 curr.tm_min,
                 curr.tm_sec);
        return buf;
    }

    string default_logfilepath = "./log/";
    string default_logfilename = "log.txt";


    class LogStrategy // 日志策略父类
    {
    public:
        virtual void RenovateLog(const string &Information) = 0; // 使用对应策略刷新日志
        virtual ~LogStrategy()
        {
        }
    };

    class ConsoleLogStrategy : public LogStrategy // 日志策略子类-向控制台打印
    {
    public:
        virtual void RenovateLog(const string &Information) // 使用对应策略刷新日志
        {
            lock_guard<mutex> m(_mutex);
            cout << Information << endl; // 向控制台打印
        }

    private:
        mutex _mutex;
    };

    class FileLogStrategy : public LogStrategy // 日志策略子类-向日志文件打印
    {
        static string default_filepath;
        static string default_filename;

    public:
        FileLogStrategy(const string &path = default_filepath, const string &name = default_filename)
            : _file_path(path),
              _file_name(name)
        {
            lock_guard<mutex> m(_mutex);        // 加锁，防止目录重复创建，导致错误
            if (filesystem::exists(_file_path)) // 判断日志目录是否存在
            {
                return;
            }
            try
            {
                filesystem::create_directories(_file_path); // 日志目录不存在，就创建
            }
            catch (const filesystem::filesystem_error &e)
            {
                cerr << e.what() << endl; // 输出错误内容
            }
        }
        virtual void RenovateLog(const string &Information) // 使用对应策略刷新日志
        {
            lock_guard<mutex> m(_mutex);

            string file = _file_path + _file_name;

            ofstream out(file, ios::app | ios::out); // 以追加方式打开指定日志文件
            if (!out.is_open())
            {
                cerr << "打开日志文件失败" << endl;
                return;
            }
            out << Information << endl; // 向指定的日志类打印
            out.close();
        }

    private:
        mutex _mutex;
        string _file_path; // 日志文件路径
        string _file_name; // 日志文件名
    };
    string FileLogStrategy::default_filepath = "./log/";
    string FileLogStrategy::default_filename = "log.txt";

    class Logger
    {
        class LogInformation // 日志信息类
        {
            typedef function<void(const string &, const LogLevel)> RenovateHandle_t;

        public:
            LogInformation(const LogLevel level, const string &file_name, int line_num, RenovateHandle_t renovate)
                : _time(CurrentTime()),
                  _log_level(level),
                  _pid(getpid()),
                  _tid(gettid()),
                  _file_name(file_name),
                  _line_num(line_num),
                  _renovate(renovate)
            {
                stringstream ss;
                ss << "[" << _time << "] " << "[" << LogLevelToString(_log_level) << "] "
                   << "[" << _pid << "] " << "[" << _tid << "] " << "[" << _file_name << "] " << "[" << _line_num << "] - ";

                _log_information = ss.str();
            }
            template <typename T>
            LogInformation &operator<<(const T &msg)
            {
                stringstream ss;
                ss << msg;
                _log_information += ss.str();
                return *this;
            }
            ~LogInformation()
            {
                _renovate(_log_information, _log_level); // 根据对应策略把日志信息刷新出去
            }

        private:
            string _time;        // 这条日志信息产生时的时间
            LogLevel _log_level; // 这条日志信息对应的日志等级
            pid_t _pid;          // 打印这条日志信息进程的pid
            pid_t _tid;          // 打印这条日志信息线程的tid
            string _file_name;   // 打印这条日志信息时所处的源文件名
            int _line_num;       // 打印这条日志信息时所处的源文件名的行号

            string _log_information; // 一条完整的日志信息

            RenovateHandle_t _renovate; // 用于回调Logger(日志类)注册进来的刷新函数
        };

    private:
        Logger()                                            // 构造函数私有
            : _strategy(make_unique<ConsoleLogStrategy>()), // 默认向控制台打印
              _value_log_level(LogLevel::DEFAULT)           // 默认等级，表示不会进行日志过滤
        {
        }
        Logger(const Logger &obj) = delete;            // 禁止拷贝构造
        Logger &operator=(const Logger &obj) = delete; // 禁止赋值

    public:
        static Logger Log; // 定义静态私有全局变量

        void EnableConsoleStrategy() // 切换刷新策略为控制台策略
        {
            lock_guard<mutex> m(_mutex); // 保护_strategy
            _strategy = make_unique<ConsoleLogStrategy>();
        }
        void EnableFileStrategy(const string &file_path = default_logfilepath, const string &file_name = default_logfilename) // 切换刷新策略为文件策略
        {
            lock_guard<mutex> m(_mutex); // 保护_strategy
            _strategy = make_unique<FileLogStrategy>(file_path, file_name);
        }
        void RenovateInformation(const string &Information, const LogLevel log_level) // 刷新日志信息
        {
            lock_guard<mutex> m(_mutex); // 保护_strategy和_value_log_level

            if (_value_log_level == LogLevel::DEFAULT) // 代表不用进行日志过滤
            {
                _strategy->RenovateLog(Information); // 使用对应策略对日志信息进行刷新
            }
            else // 代表需要进行  日志过滤
            {
                if (log_level == _value_log_level) // 判断当前这条日志信息的等级是否为用户最关注的（_value_log_level）
                {
                    _strategy->RenovateLog(Information); // 使用对应策略对日志信息进行刷新
                }
            }
        }
        LogInformation operator()(LogLevel level, const string &file_name, int line_num)
        {
            return LogInformation(level, file_name, line_num, [this](const string &Information, const LogLevel log_level)
                                  { this->RenovateInformation(Information, log_level); });
        }
        void SetValueLogLevel(const LogLevel level)
        {
            lock_guard<mutex> m(_mutex); // 保护_value_log_level
            _value_log_level = level;
        }

    private:
        unique_ptr<LogStrategy> _strategy; // 刷新策略
        LogLevel _value_log_level;         // 用户只想看（最关注的）的日志等级,用于日志过滤
        mutex _mutex;
    };

    Logger Logger::Log; // 定义日志全局变量

#define LOG(Level) (Logger::Log(Level, __FILE__, __LINE__)) // 使用日志

    void EnableConsoleStrategy() // 切换刷新策略为控制台策略
    {
        Logger::Log.EnableConsoleStrategy();
    }
    void EnableFileStrategy(const string &FilePath = default_logfilepath, const string &FileName = default_logfilename) // 切换刷新策略为文件策略
    {
        Logger::Log.EnableFileStrategy(FilePath, FileName);
    }
    // 设置用户只想看（最关注的）的日志等级
    // 设置之后  只会  输出该日志等级的日志信息
    void SetValueLogLevel(const LogLevel level)
    {
        Logger::Log.SetValueLogLevel(level);
    }
}